﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OWLServer.owl.tipos;
using System.Xml.Linq;
using OWLServer.owl.etiquetas;

namespace OWLServer.owl.utilidades
{
    /// <summary>
    /// Clase que contiene métodos para manejar las clases de una ontología.
    /// </summary>
    class UtilidadesClases
    {
        /// <summary>
        /// Construye las clase del OWL
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        public static void construirClases(Ontologia ontologia, XElement root)
        {
            Clase clase;

            IDictionary<string, EspacioNombres> espacioNombresDictionary = ontologia.getEspaciosNombresDictiorary();
            if (UtilidadesEspaciosNombres.contieneOWL(espacioNombresDictionary) && UtilidadesEspaciosNombres.contieneRDF(espacioNombresDictionary) && UtilidadesEspaciosNombres.contieneRDFS(espacioNombresDictionary))
            {
                // Extraemos los elementos de las clases
                contruirIdClases(ontologia, root, espacioNombresDictionary);

                // Vemos si existen las clases Especiales del dominio OWL
                crearClasesEspeciales(ontologia, espacioNombresDictionary);

                // Extraemos los elementos de las clases con subclases, equivalentes, disjuntas
                IEnumerable<XElement>  eleClases =
                    from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.CLASE)
                    where el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.SUBCLASE).Count() > 0
                    || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.CLASE_EQUIVALENTE).Count() > 0
                    || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.DISJUNTA_CON).Count() > 0
                    || el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.UNION_DISJUNTAS).Count() > 0
                    select el;

                foreach (XElement ele in eleClases)
                {
                    Elemento about = UtilidadesElementos.getElemento(UtilidadesElementos.getId(ele, espacioNombresDictionary), ontologia);
                    clase = UtilidadesClases.getClase(ontologia, about);

                    // Comprobamos si son subclases de alguna clase
                    extraerSubClases(ontologia, clase, ele, espacioNombresDictionary);

                    // Comprobamos si es equivalente a alguna clase
                    extraerClasesEquivalentes(ontologia, clase, ele, espacioNombresDictionary);

                    // Comprobamos si es disjunta a alguna clase
                    extraerClasesDisjuntas(ontologia, clase, ele, espacioNombresDictionary);

                    // Obtenemos las uniones de clases disjuntas
                    extraerUnionClasesDisjuntas(ontologia, ele, clase);
                }
                // Obtenemos los grupos de clases disjuntas
                extraerGruposClasesDisjuntas(ontologia, root);

                // Una vez que se han creado todas las clases vemos si estan heredando de la case cosa.
                // Todas las clases son hijas de la clase cosa.
                relacionarClaseConCosa(ontologia, espacioNombresDictionary);
            }
        }

        /// <summary>
        /// Contruye las clases de la ontología a aprtir del Id
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        /// <param name="espacioNombresDictionary">Diccionario con los espacios de nombres de la ontologia</param>
        private static void contruirIdClases(Ontologia ontologia, XElement root, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Clase clase;

            // Extraemos los elementos de las clases
            IEnumerable<XElement> eleClases =
                from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.CLASE)
                select el;

            // Creamos los objetos Clase a partir de los elementos recuperados
            foreach (XElement ele in eleClases)
            {
                clase = new Clase(UtilidadesElementos.getElemento(UtilidadesElementos.getId(ele, espacioNombresDictionary), ontologia));

                ontologia.Clases.Add(clase);
            }
        }

        /// <summary>
        /// Extrae las subclases de la clase que le pasemos
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="clase">Clase</param>
        /// <param name="ele">Elemento que representa a la Clase en el OWL</param>
        /// <param name="espacioNombresDictionary">Diccionario con los espacios de nombres de la ontologia</param>
        private static void extraerSubClases(Ontologia ontologia, Clase clase, XElement ele, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Elemento recurso;
            Clase claseRecurso;

            IEnumerable<XElement> eleSubClases =
                        from el in ele.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasRDFS.NOMBRE]) + EtiquetasRDFS.SUBCLASE)
                        select el;

            foreach (XElement eleSub in eleSubClases)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleSub, espacioNombresDictionary), ontologia);

                if (null != recurso) // Puede que sea una restriccion
                {
                    claseRecurso = UtilidadesClases.getClase(ontologia, recurso);
                    clase.PadreClases.Add(claseRecurso);
                    claseRecurso.HijasClases.Add(clase);
                }
            }
        }

        /// <summary>
        /// Extrae las clases equivalentes de la clase que le pasemos
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="clase">Clase</param>
        /// <param name="ele">Elemento que representa a la Clase en el OWL</param>
        /// <param name="espacioNombresDictionary">Diccionario con los espacios de nombres de la ontologia</param>
        private static void extraerClasesEquivalentes(Ontologia ontologia, Clase clase, XElement ele, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Elemento recurso;
            Clase claseRecurso;

            IEnumerable<XElement> eleEqvClases =
                        from el in ele.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.CLASE_EQUIVALENTE)
                        select el;

            foreach (XElement eleEqv in eleEqvClases)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleEqv, espacioNombresDictionary), ontologia);

                if (null != recurso) // Puede que sea una restriccion
                {
                    claseRecurso = UtilidadesClases.getClase(ontologia, recurso);
                    clase.Equivalentes.Add(claseRecurso);
                    claseRecurso.Equivalentes.Add(clase);
                }
            }
        }

        /// <summary>
        /// Extrae las clases disjuntas de la clase que le pasemos
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="clase">Clase</param>
        /// <param name="ele">Elemento que representa a la Clase en el OWL</param>
        /// <param name="espacioNombresDictionary">Diccionario con los espacios de nombres de la ontologia</param>
        private static void extraerClasesDisjuntas(Ontologia ontologia, Clase clase, XElement ele, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Elemento recurso;
            Clase claseRecurso;

            IEnumerable<XElement> eleDisjClases =
                        from el in ele.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.DISJUNTA_CON)
                        select el;

            foreach (XElement eleDisj in eleDisjClases)
            {
                recurso = UtilidadesElementos.getElemento(UtilidadesElementos.getNombreRecurso(eleDisj, espacioNombresDictionary), ontologia);
                claseRecurso = UtilidadesClases.getClase(ontologia, recurso);
                clase.Disjuntas.Add(claseRecurso);
                claseRecurso.Disjuntas.Add(clase);
            }
        }

        /// <summary>
        /// Extrae las uniones de clases disjuntas
        /// </summary>
        /// <param name="ontologia">Ontolología con la que estamos tratando</param>
        /// <param name="root">Documento OWL de la ontología</param>
        /// <param name="clase">Clase con la que lo vamos a relacionar</param>
        private static void extraerUnionClasesDisjuntas(Ontologia ontologia, XElement eleClase, Clase clase)
        {
            // Recorremos las uniones de clases disjuntas que hay
            IEnumerable<XElement> eleUnionesDisjuntas =
                from el in eleClase.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.UNION_DISJUNTAS)
                select el;

            List<Clase> unionDisjuntos;
            IEnumerable<XElement> elesUnion;
            Elemento elemento;

            // Recorremos las uniones de clases disjuntas
            foreach (XElement eleUnionDisjunta in eleUnionesDisjuntas)
            {
                /// Suponemos que es una colección por eso no se comprueba el atributo rdf:parseType="Collection"

                elesUnion =
                from el in eleUnionDisjunta.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                select el;

                unionDisjuntos = new List<Clase>();
                // Obtenemos la lista de elementos que son de la union de distintos
                foreach (XElement eleDist in elesUnion)
                {
                    elemento = UtilidadesElementos.getElemento(UtilidadesElementos.getId(eleDist, ontologia.getEspaciosNombresDictiorary()), ontologia);

                    unionDisjuntos.Add(UtilidadesClases.getClase(ontologia, elemento));
                }

                clase.UnionesClasesDisjuntas.Add(new UnionClasesDisjuntas(unionDisjuntos));
            }
        }

        /// <summary>
        /// Extrae los grupos de clases disjuntas
        /// </summary>
        /// <param name="ontologia">Ontolología con la que estamos tratando</param>
        /// <param name="root">Documento OWL de la ontología</param>
        private static void extraerGruposClasesDisjuntas(Ontologia ontologia, XElement root)
        {
            bool hayDisjuntos = false;
            IEnumerable<XElement> eleDisjuntos;
            IEnumerable<XElement> eleDisjunto;
            List<Clase> listaDisjuntos;
            Elemento elemento;

            // Recorremos als descripciones que hay
            IEnumerable<XElement> eleDescripciones =
                from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                select el;

            // Recorremos las Descripciones
            foreach (XElement eleDescripcion in eleDescripciones)
            {
                hayDisjuntos = UtilidadesElementos.isElementoDescripcionTipo(eleDescripcion, EtiquetasOWL.TODAS_CLASES_DISJUNTAS, ontologia);

                // Si es de tipo disjunto extraemos la coleccion
                if (hayDisjuntos)
                {
                    eleDisjuntos =
                    from el in eleDescripcion.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.MIEMBROS)
                    select el;

                    /// Suponemos que es una colección por eso no se comprueba el atributo rdf:parseType="Collection"

                    eleDisjunto =
                    from el in eleDisjuntos.Elements(UtilidadesEspaciosNombres.getEspacioNombres(ontologia.getEspaciosNombresDictiorary()[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                    select el;

                    listaDisjuntos = new List<Clase>();
                    // Obtenemos la lista de elementos que son distintos unos con otros
                    foreach (XElement eleDist in eleDisjunto)
                    {
                        elemento = UtilidadesElementos.getElemento(UtilidadesElementos.getId(eleDist, ontologia.getEspaciosNombresDictiorary()), ontologia);

                        listaDisjuntos.Add(UtilidadesClases.getClase(ontologia, elemento));
                    }
                    // Vinculamos las clases disjuntas. Todas con todas
                    foreach (Clase disjunto in listaDisjuntos)
                    {
                        foreach (Clase disjuntoA in listaDisjuntos)
                        {
                            if (!disjunto.Equals(disjuntoA))
                            {
                                disjunto.Disjuntas.Add(disjuntoA);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Recupera las Claves de las Clases
        /// Se tiene que ejecutar cuando de hayan cargado las clases y las propiedades.
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="root">Raiz del documento OWL</param>
        public static void contruirClavesClase(Ontologia ontologia, XElement root)
        {
            Elemento about;
            Propiedad propiedad = null;
            Elemento aboutClase;
            IEnumerable<XElement> elesClave;
            IEnumerable<XElement> elesDescripcion;
            Clase claseAbout;

            // Obtenemos los diccionarios
            IDictionary<string, EspacioNombres> espacioNombresDictionary = ontologia.getEspaciosNombresDictiorary();
            IDictionary<string, Propiedad> dicPropiedadObjeto = ontologia.getPropiedadesObjetosDictiorary();
            IDictionary<string, Propiedad> dicPropiedadDatos = ontologia.getPropiedadesDatosDictiorary();

            // Extraemos los elementos de las clases
            IEnumerable<XElement> eleClases =
                from el in root.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.CLASE)
                where el.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.ES_CLAVE).Count() > 0 
                select el;

            // Recorremos las clases para buscar las claves
            foreach (XElement eleClase in eleClases)
            {
                aboutClase = UtilidadesElementos.getElemento(UtilidadesElementos.getId(eleClase, espacioNombresDictionary), ontologia);
                claseAbout = UtilidadesClases.getClase(ontologia, aboutClase);

                elesClave =
                    from el in eleClase.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasOWL.NOMBRE]) + EtiquetasOWL.ES_CLAVE)
                    select el;

                // Recorremos las claves que tenga la clase
                foreach (XElement eleClave in elesClave)
                {
                    elesDescripcion =
                    from el in eleClave.Elements(UtilidadesEspaciosNombres.getEspacioNombres(espacioNombresDictionary[EtiquetasRDF.NOMBRE]) + EtiquetasRDF.DESCRIPCION)
                    select el;

                    // Obtenemos la decripcion de la clave de la clase
                    foreach (XElement eleDescrp in elesDescripcion)
                    {
                        about = UtilidadesElementos.getElemento(UtilidadesElementos.getId(eleDescrp, espacioNombresDictionary), ontologia);

                        // Las propiedades, sean del tipo que sean, tienen un nombre unico.
                        // Solo pueden ser Keys propiedades que pertenezcan a la misma ontologia
                        // Recogemos la propiedad segun su tipo
                        if (dicPropiedadObjeto.ContainsKey(about.ToString()))
                        {
                            propiedad = dicPropiedadObjeto[about.ToString()];
                            claseAbout.ClavesPropiedadObjeto.Add(propiedad as PropiedadObjeto);
                        }
                        else if (dicPropiedadDatos.ContainsKey(about.ToString()))
                        {
                            propiedad = dicPropiedadDatos[about.ToString()];
                            claseAbout.ClavesPropiedadDato.Add(propiedad);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Obtiene la lista de clases hijas y descendientes, sin repetidos, de la lista de clases que le pasemos.
        /// </summary>
        /// <param name="clases">Lista de clases de las que se quieren obtener los hijos</param>
        /// <param name="listaClasesHijos">lista de clases hijas que se está construyendo.</param>
        public static void obtenerClasesHijo(IList<Clase> clases, IList<Clase> listaClasesHijos)
        {
            foreach (Clase clase in clases)
            {
                obtenerClasesHijo(clase, listaClasesHijos);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases hijas y descendientes, sin repetidos, de la clase que le pasemos
        /// </summary>
        /// <param name="clase">Clase de la que se quieren obtener los hijos</param>
        /// <param name="listaClasesHijos">lista de clases hijas que se está construyendo.</param>
        public static void obtenerClasesHijo(Clase clase, IList<Clase> listaClasesHijos)
        {
            if (!listaClasesHijos.Contains(clase))
            {
                listaClasesHijos.Add(clase);
                obtenerClasesHijo(clase.HijasClases, listaClasesHijos);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases padre y antecesores, sin repetidos, de la lista de clases que le pasemos
        /// </summary>
        /// <param name="clases">Lista de clases de las que se quieren obtener los padres</param>
        /// <param name="listaClasesPadre">lista de clases padre que se está construyendo.</param>
        public static void obtenerClasesPadre(IList<Clase> clases, IList<Clase> listaClasesPadre)
        {
            foreach (Clase clase in clases)
            {
                obtenerClasesPadre(clase, listaClasesPadre);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases padre y antecesores, sin repetidos, de la clase que le pasemos
        /// </summary>
        /// <param name="clase">Clase de la que se quieren obtener los padres</param>
        /// <param name="listaClasesPadre">Lista de clases padre que se está construyendo.</param>
        public static void obtenerClasesPadre(Clase clase, IList<Clase> listaClasesPadre)
        {
            if (!listaClasesPadre.Contains(clase))
            {
                listaClasesPadre.Add(clase);
                obtenerClasesPadre(clase.PadreClases, listaClasesPadre);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases equivalentes, sin repetidos, de la lista de clases que le pasemos
        /// </summary>
        /// <param name="clases">Lista de clases de las que se quieren obtener los equivalentes</param>
        /// <param name="listaClasesEquivalentes">Lista de clases equivalentes obtenidas.</param>
        public static void obtenerClasesEquivalentes(IList<Clase> clases, IList<Clase> listaClasesEquivalentes)
        {
            foreach (Clase clase in clases)
            {
                obtenerClasesEquivalentes(clase, listaClasesEquivalentes);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases equivalentes, sin repetidos, de la clase que le pasemos
        /// </summary>
        /// <param name="clase">Clase de la que se quieren obtener los equivalentes</param>
        /// <param name="listaClasesEquivalentes">Lista de clases equivalentes obtenidas.</param>
        public static void obtenerClasesEquivalentes(Clase clase, IList<Clase> listaClasesEquivalentes)
        {
            if (!listaClasesEquivalentes.Contains(clase))
            {
                listaClasesEquivalentes.Add(clase);
                obtenerClasesEquivalentes(clase.Equivalentes, listaClasesEquivalentes);
            }
        }

        /// <summary>
        /// Obtiene la lista de clases disjuntas, sin repetidos, de la lista de clases que le pasemos
        /// </summary>
        /// <param name="clases">Lista de clases de las que se quieren obtener las disjuntas</param>
        /// <param name="listaClasesDisjuntas">Lista de clases disjuntas a contruir.</param>
        public static void obtenerClasesDisjuntas(IList<Clase> clases, IList<Clase> listaClasesDisjuntas)
        {
            IList<Clase> disjuntas;
            foreach (Clase clase in clases)
            {
                disjuntas = clase.Disjuntas;
                disjuntas = obtenerHijosYEquivalentes(disjuntas);
                foreach (Clase disjunta in disjuntas)
                {
                    if (!listaClasesDisjuntas.Contains(disjunta))
                    {
                        listaClasesDisjuntas.Add(disjunta);
                    }
                }
            }
        }

        /// <summary>
        /// Devuelve la lista de clases que son hijo o equivalente a la que le pasamos
        /// </summary>
        /// <param name="clase">Clase de las que vamos a obtener sus hijos y equivalentes</param>
        /// <returns>Lista de hijos y equivalentes.</returns>
        public static IList<Clase> obtenerHijosYEquivalentes(Clase clase)
        {
            IList<Clase> clasesHijas = new List<Clase>();
            IList<Clase> clasesEquivalentes = new List<Clase>();
            obtenerClasesHijo(clase, clasesHijas);
            obtenerClasesEquivalentes(clase, clasesEquivalentes);

            return unirListaClasesSinDuplicados(clasesEquivalentes, clasesHijas);
        }

        /// <summary>
        /// Devuelve la lista de clases que son hijo o equivalente a las que le pasamos.
        /// </summary>
        /// <param name="clase">Clases de las que vamos a obtener sus hijos y equivalentes</param>
        /// <returns>Lista de hijos y equivalentes.</returns>
        public static IList<Clase> obtenerHijosYEquivalentes(IList<Clase> clases)
        {
            IList<Clase> clasesHijas = new List<Clase>();
            IList<Clase> clasesEquivalentes = new List<Clase>();
            obtenerClasesHijo(clases, clasesHijas);
            obtenerClasesEquivalentes(clases, clasesEquivalentes);

            return unirListaClasesSinDuplicados(clasesEquivalentes, clasesHijas);
        }

        /// <summary>
        /// Devuelve la lista de clases que son padre o equivalente a la que le pasamos
        /// </summary>
        /// <param name="clase">Clase de las que vamos a obtener sus padres y equivalentes</param>
        /// <returns>Lista de padres y equivalentes.</returns>
        public static IList<Clase> obtenerPadresYEquivalentes(Clase clase)
        {
            IList<Clase> clasesPadres = new List<Clase>();
            IList<Clase> clasesEquivalentes = new List<Clase>();
            obtenerClasesPadre(clase, clasesPadres);
            obtenerClasesEquivalentes(clase, clasesEquivalentes);

            return unirListaClasesSinDuplicados(clasesEquivalentes, clasesPadres);
        }

        /// <summary>
        /// Devuelve la lista de padre que son hijo o equivalente a las que le pasamos.
        /// </summary>
        /// <param name="clase">Clases de las que vamos a obtener sus padres y equivalentes</param>
        /// <returns>Lista de padres y equivalentes.</returns>
        public static IList<Clase> obtenerPadresYEquivalentes(IList<Clase> clases)
        {
            IList<Clase> clasesPadres = new List<Clase>();
            IList<Clase> clasesEquivalentes = new List<Clase>();
            obtenerClasesPadre(clases, clasesPadres);
            obtenerClasesEquivalentes(clases, clasesEquivalentes);

            return unirListaClasesSinDuplicados(clasesEquivalentes, clasesPadres);
        }

        /// <summary>
        /// Construye una lista que esta compuesta de las dos que le pasemos pero sin dublicados
        /// </summary>
        /// <param name="lista1">Lista 1</param>
        /// <param name="lista2">Lista 2</param>
        /// <returns>Lista sin repetidos</returns>
        private static IList<Clase> unirListaClasesSinDuplicados(IList<Clase> lista1, IList<Clase> lista2)
        {
            return lista1.Union(lista2, new ComparadorClase()).ToList<Clase>();
        }

        /// <summary>
        /// Nos indica si algun elemento de la lista 1 de clases esta contenido en la lista 2
        /// </summary>
        /// <param name="lista1">Lista de clases 1</param>
        /// <param name="lista2">Lista de clases 2</param>
        /// <returns>Si tienen alguna clase en común.</returns>
        public static bool tienenAlgunaClaseEnComun(List<Clase> lista1, List<Clase> lista2)
        {
            bool enComun = false;

            for (int i = 0; i < lista1.Count && !enComun; i++)
            {
                enComun = lista2.Contains(lista1[i]);
            }

            return enComun;
        }

        /// <summary>
        /// Compueba si existe las clases especiales como Thing y NamedIndividual del OWL.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearClasesEspeciales(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            crearClaseCosa(ontologia, espacioNombresDictionary);
            crearClaseNombreIndividual(ontologia, espacioNombresDictionary);
        }

        /// <summary>
        /// Compueba si existe la clase Thing y sino exite la añade.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearClaseCosa(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Clase cosa = new Clase(EtiquetasOWL.COSA, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            if (!ontologia.Clases.Contains(cosa))
            {
                ontologia.Clases.Add(cosa);
            }
        }

        /// <summary>
        /// Compueba si existe la clase Nombre Individual y sino exite la añade.
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        /// <param name="espacioNombresDictionary">Espacios de nombres de la ontología</param>
        private static void crearClaseNombreIndividual(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Clase nombreIndividual = new Clase(EtiquetasOWL.NOMBRE_INDIVIDUAL, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            if (!ontologia.Clases.Contains(nombreIndividual))
            {
                ontologia.Clases.Add(nombreIndividual);
            }
        }

        /// <summary>
        /// Compueba que todas las clases depcienden de Thing y si no es así las relaciona
        /// </summary>
        /// <param name="ontologia">Ontología de entrada</param>
        private static void relacionarClaseConCosa(Ontologia ontologia, IDictionary<string, EspacioNombres> espacioNombresDictionary)
        {
            Clase cosa = new Clase(EtiquetasOWL.COSA, espacioNombresDictionary[EtiquetasOWL.NOMBRE]);
            cosa = ontologia.getClasesDictiorary()[cosa.ToString()];
            foreach (Clase clase in ontologia.Clases)
            {
                if (!clase.PadreClases.Contains(cosa) && !clase.Equals(cosa))
                {
                    cosa.HijasClases.Add(clase);
                    clase.PadreClases.Add(cosa);
                }
            }
        }

        /// <summary>
        /// Nos dice si las dos lista de clases son iguales
        /// </summary>
        /// <param name="lista1">Listado 1</param>
        /// <param name="lista2">Listado 2</param>
        /// <returns></returns>
        public static bool sonIguales(IList<Clase> lista1, IList<Clase> lista2)
        {
            bool iguales = false;
            IList<Clase> listaUnion;

            lista1 = obtenerHijosYEquivalentes(lista1);
            lista2 = obtenerHijosYEquivalentes(lista2);

            if (lista1.Count == lista2.Count)
            {
                listaUnion = unirListaClasesSinDuplicados(lista1, lista2);
                iguales = lista1.Count == listaUnion.Count;
            }

            return iguales;
        }

        /// <summary>
        /// Obtiene una clase de la ontologia que representa el Elemento
        /// </summary>
        /// <param name="ontologia">Ontología</param>
        /// <param name="elemento">Elemento que representa la clase</param>
        /// <returns>Clase que representa al elemento</returns>
        public static Clase getClase(Ontologia ontologia, Elemento elemento)
        {
            IDictionary<string, Clase> clasesDictiorary = ontologia.getClasesDictiorary(); // Clases almacenadas en un diccionario
            Clase clase = null;

            if (clasesDictiorary.ContainsKey(elemento.ToString()))
            {
                clase = clasesDictiorary[elemento.ToString()];
            }
            else
            {
                clase = new Clase(elemento);
                ontologia.Clases.Add(clase);
            }

            return clase;
        }
    }

    /// <summary>
    /// Comparados de clases
    /// </summary>
    public class ComparadorClase : IEqualityComparer<Clase>
    {

        #region Miembros de IEqualityComparer<Clase>

        public bool Equals(Clase x, Clase y)
        {
            return x.Equals(y);
        }

        public int GetHashCode(Clase obj)
        {
            return obj.ToString().GetHashCode();
        }

        #endregion
    }
}